package com.enation.app.javashop.core.promotion.luck.service.impl;

import com.alibaba.fastjson.JSON;
import com.dag.eagleshop.core.account.utils.HttpUtils;
import com.dag.eagleshop.core.delivery.model.dto.JsonBean;
import com.enation.app.javashop.core.aftersale.model.enums.RefundStatusEnum;
import com.enation.app.javashop.core.base.model.enums.YesNoEnum;
import com.enation.app.javashop.core.distribution.model.enums.DistributionMissionStatusEnums;
import com.enation.app.javashop.core.member.model.dos.Member;
import com.enation.app.javashop.core.member.service.MemberManager;
import com.enation.app.javashop.core.promotion.coupon.service.CouponManager;
import com.enation.app.javashop.core.promotion.luck.model.dos.LuckDO;
import com.enation.app.javashop.core.promotion.luck.model.dos.LuckPrizeDO;
import com.enation.app.javashop.core.promotion.luck.model.dos.LuckRecordDO;
import com.enation.app.javashop.core.promotion.luck.model.enums.LuckStatusEnums;
import com.enation.app.javashop.core.promotion.luck.model.enums.PrizeTypeEnums;
import com.enation.app.javashop.core.promotion.luck.model.vo.LuckTotalTime;
import com.enation.app.javashop.core.promotion.luck.model.vo.QueryLuckRecordVO;
import com.enation.app.javashop.core.promotion.luck.model.vo.QueryLuckVO;
import com.enation.app.javashop.core.promotion.luck.service.LuckManager;
import com.enation.app.javashop.core.promotion.luck.service.LuckPrizeManager;
import com.enation.app.javashop.core.promotion.luck.service.LuckRecordManager;
import com.enation.app.javashop.core.promotion.luck.utils.ProbabilityUtils;
import com.enation.app.javashop.core.shop.model.dos.Clerk;
import com.enation.app.javashop.core.shop.service.ClerkManager;
import com.enation.app.javashop.core.system.sendMessage.WechatSendMessage;
import com.enation.app.javashop.core.trade.order.model.dos.OrderDO;
import com.enation.app.javashop.core.trade.order.model.dto.OrderQueryParam;
import com.enation.app.javashop.core.trade.order.model.enums.PayStatusEnum;
import com.enation.app.javashop.core.trade.order.service.OrderQueryManager;
import com.enation.app.javashop.core.trade.sdk.model.OrderDetailDTO;
import com.enation.app.javashop.framework.cache.Cache;
import com.enation.app.javashop.framework.context.UserContext;
import com.enation.app.javashop.framework.database.DaoSupport;
import com.enation.app.javashop.framework.database.Page;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.security.model.Buyer;
import com.enation.app.javashop.framework.security.model.Seller;
import com.enation.app.javashop.framework.util.DateUtil;
import com.enation.app.javashop.framework.util.IDataUtils;
import com.enation.app.javashop.framework.util.SqlSplicingUtil;
import com.enation.app.javashop.framework.util.StringUtil;
import io.jsonwebtoken.lang.Collections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.unit.DataUnit;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 孙建
 */
@Service
public class LuckManagerImpl implements LuckManager {

    @Autowired
    @Qualifier("tradeDaoSupport")
    private DaoSupport daoSupport;
    @Autowired
    private LuckPrizeManager luckPrizeManager;
    @Autowired
    private LuckRecordManager luckRecordManager;
    @Autowired
    private OrderQueryManager orderQueryManager;
    @Autowired
    private MemberManager memberManager;
    @Autowired
    private CouponManager couponManager;
    @Autowired
    private Cache cache;
    @Autowired
    private ClerkManager clerkManager;

    @Value("${service.webSocket.url}")
    private String url;

    /**
     * 使用webSocket推消息
     */
    private static final String WEB_SOCKET = "/websocket/websocket_push_message";

    @Override
    public Page<LuckDO> queryLuckList(QueryLuckVO queryLuckVO) {
        // 1.组装查询条件参数
        StringBuilder sql = new StringBuilder("select * from es_luck");

        List<Object> termList = new ArrayList<>();
        List<String> sqlSplit = new ArrayList<>();

        Integer sellerId = queryLuckVO.getSellerId();
        if (sellerId != null) {
            sqlSplit.add(" seller_id=? ");
            termList.add(sellerId);
        }

        if (queryLuckVO.getLuckStatus() != null) {
            sqlSplit.add(" luck_status=? ");
            termList.add(queryLuckVO.getLuckStatus());
        }

        sqlSplit.add(" is_delete=? ");
        termList.add(YesNoEnum.NO.getIndex());

        // 团长任务名称查询
        String luckName = queryLuckVO.getLuckName();
        if (StringUtil.notEmpty(luckName)) {
            sqlSplit.add(" luck_name like ? ");
            termList.add("%" + luckName + "%");
        }

        // 按时间查询
        Long startTime = queryLuckVO.getStartTime();
        if (!ObjectUtils.isEmpty(startTime)) {
            sqlSplit.add(" start_time >= ? ");
            termList.add(startTime);
        }
        Long endTime = queryLuckVO.getEndTime();
        if (!ObjectUtils.isEmpty(endTime)) {
            sqlSplit.add(" end_time <= ? ");
            termList.add(endTime);
        }

        // 2.对sql条件语句进行拼接并查询
        String sqlSplicing = SqlSplicingUtil.sqlSplicing(sqlSplit);
        if (StringUtil.notEmpty(sqlSplicing)) {
            sql.append(sqlSplicing);
        }
        sql.append(" order by create_time desc");
        return this.daoSupport.queryForPage(sql.toString(), queryLuckVO.getPageNo(), queryLuckVO.getPageSize(),
                LuckDO.class, termList.toArray());
    }

    @Override
    public LuckDO getByLuckId(Integer luckId) {
        return daoSupport.queryForObject(LuckDO.class, luckId);
    }

    // 下线已经过期的抽奖活动
    @Override
    public void expireLuck() {
        String sql = "select luck_id from es_luck where end_time <= ? ";
        List<Integer> luckIdList = this.daoSupport.queryForList(sql, DateUtil.getDateline());
        if (!CollectionUtils.isEmpty(luckIdList)) {
            String updateSql = "update es_luck set luck_status = ? where luck_id in (?)";
            this.daoSupport.execute(updateSql, LuckStatusEnums.OFFLINE.getIndex(), StringUtil.listToString(luckIdList, ","));
        }
    }

    @Override
    public void onOffLine(Integer luckId, Integer luckStatus) {
        // 如果是上线任务，判断时候当前店铺已经有已上线的任务
        if (LuckStatusEnums.ONLINE.getIndex() == luckStatus) {
            Seller seller = UserContext.getSeller();
            QueryLuckVO queryLuckVO = new QueryLuckVO();
            queryLuckVO.setPageNo(1);
            queryLuckVO.setPageSize(10);
            queryLuckVO.setSellerId(seller.getSellerId());
            queryLuckVO.setLuckStatus(LuckStatusEnums.ONLINE.getIndex());
            Page<LuckDO> page = queryLuckList(queryLuckVO);
            List<LuckDO> luckList = page.getData();
            if (!CollectionUtils.isEmpty(luckList)) {
                luckList = luckList.stream().filter(luckDO -> luckDO.getEndTime() > DateUtil.getDateline()).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(luckList)) {
                    List<String> names = luckList.stream().map(LuckDO::getLuckName).collect(Collectors.toList());
                    throw new ServiceException("500", "抽奖活动【" + StringUtil.listToString(names, "，") + "】已上线，同时只能有一个抽奖活动在线！");
                }
            }
        }

        LuckDO luckDO = getByLuckId(luckId);
        luckDO.setLuckStatus(luckStatus);
        daoSupport.update(luckDO, luckId);
    }


    @Override
    public Map queryLuckIndex(Integer memberId) {
        Map<String, Object> resultMap = new HashMap<>();

        // 查询最新的抽奖活动
        LuckDO luckDO = queryLatestLuck();
        if (luckDO != null) {
            // 查询抽奖奖品
            List<LuckPrizeDO> luckPrizeList = luckPrizeManager.queryListByLuckId(luckDO.getLuckId());

            // 查询剩余抽奖次数
            int todayLeftLuckTimes = this.getTodayLeftLuckTimes(luckDO, memberId);

            resultMap.put("luck", luckDO);
            resultMap.put("luckPrizeList", luckPrizeList);
            resultMap.put("todayLeftLuckTimes", todayLeftLuckTimes);
        }
        return resultMap;
    }


    @Override
    public LuckDO queryLatestLuck() {
        QueryLuckVO queryLuckVO = new QueryLuckVO();
        queryLuckVO.setLuckStatus(DistributionMissionStatusEnums.ONLINE.getIndex());
        queryLuckVO.setPageNo(1);
        queryLuckVO.setPageSize(1);
        Page<LuckDO> page = queryLuckList(queryLuckVO);
        List<LuckDO> luckList = page.getData();
        if (!Collections.isEmpty(luckList)) {
            return luckList.get(0);
        }
        return null;
    }


    /**
     * 点击开始抽奖
     */
    @Override
    public Map startLuck(Integer luckId, Integer memberId) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("success", false);
        resultMap.put("message", "抽奖失败，请稍后再试");

        // 校验抽奖活动配置
        LuckDO luckDO = getByLuckId(luckId);
        if (luckDO == null || luckDO.getLuckStatus() == LuckStatusEnums.OFFLINE.getIndex()) {
            resultMap.put("message", "抽奖活动已结束，请下次再来吧！");
            return resultMap;
        }
        if (luckDO.getLuckStatus() == LuckStatusEnums.NEW.getIndex()) {
            resultMap.put("message", "抽奖活动未开始，请耐心等待！");
            return resultMap;
        }
        List<LuckPrizeDO> luckPrizeList = luckPrizeManager.queryListByLuckId(luckId);
        if (IDataUtils.isEmpty(luckPrizeList) || luckPrizeList.size() != 8) {
            resultMap.put("message", "抽奖活动配置有误，请稍后再试！");
            return resultMap;
        }

        // 判断剩余抽奖次数
        int todayLeftLuckTimes = this.getTodayLeftLuckTimes(luckDO, memberId);
        if (todayLeftLuckTimes <= 0) {
            resultMap.put("message", "您的抽奖次数已用完了哦");
            return resultMap;
        }

        // 异常概率重新分布
        this.probabilityAutoSet(luckPrizeList);

        // 开始执行抽奖算法
        int size = luckPrizeList.size();
        Map<Integer, LuckPrizeDO> luckPrizeMap = new HashMap<>();
        List<Double> probList = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            LuckPrizeDO luckPrize = luckPrizeList.get(i);
            luckPrizeMap.put(i, luckPrize);
            probList.add(luckPrize.getProbability().doubleValue());
        }
        ProbabilityUtils probabilityUtils = new ProbabilityUtils(probList);
        int probIndex = probabilityUtils.next();
        LuckPrizeDO gotLuckPrize = luckPrizeMap.get(probIndex);

        // 缓存并返回结果
        String exchangeId = UUID.randomUUID().toString().replace("-", "");
        cache.put(exchangeId, gotLuckPrize, 60);
        resultMap.put("success", true);
        resultMap.put("message", "ok");
        resultMap.put("luckPrize", gotLuckPrize);
        resultMap.put("exchangeId", exchangeId);
        resultMap.put("isHit", gotLuckPrize.getPrizeType() != PrizeTypeEnums.THANKS.getIndex());
        return resultMap;
    }


    /**
     * 确认抽奖结果
     */
    @Override
    public Map confirmLuck(Integer luckId, String exchangeId, Integer memberId) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("success", false);
        resultMap.put("message", "兑奖失败，请重新抽奖");

        // 校验抽奖活动配置
        LuckDO luckDO = getByLuckId(luckId);
        if (luckDO == null) {
            resultMap.put("message", "抽奖活动已结束，请下次再来吧！");
            return resultMap;
        }

        // 获取中奖奖品
        LuckPrizeDO gotLuckPrize = (LuckPrizeDO) cache.get(exchangeId);
        if (gotLuckPrize == null) {
            return resultMap;
        }

        // 生成抽奖记录
        Member member = memberManager.getModel(memberId);
        LuckRecordDO luckRecordDO = luckRecordManager.saveLuckRecord(member, luckDO, gotLuckPrize);

        // 查询剩余次数
        int todayLeftLuckTimes = this.getTodayLeftLuckTimes(luckDO, memberId);
        resultMap.put("success", true);
        resultMap.put("message", "本次抽奖完成");
        resultMap.put("todayLeftLuckTimes", todayLeftLuckTimes);
        resultMap.put("isHit", gotLuckPrize.getPrizeType() != PrizeTypeEnums.THANKS.getIndex());

        // 清除缓存
        cache.remove(exchangeId);

        // 奖品是优惠券的自动处理
        Integer prizeType = luckRecordDO.getPrizeType();
        if (prizeType == PrizeTypeEnums.COUPON.getIndex()) {
            // 发放优惠券
            couponManager.issueVouchersToUsers(member, gotLuckPrize.getCouponId());

            Integer prizeNum = gotLuckPrize.getPrizeNum();
            if (prizeNum > 0) {
                gotLuckPrize.setPrizeNum(prizeNum - 1);
                // 执行数据库-更新奖品数量
                luckPrizeManager.updateLuckPrize(gotLuckPrize);
            }
        }

        if (gotLuckPrize.getPrizeType() != PrizeTypeEnums.THANKS.getIndex()) {
            // 推送webSocket
            Map<String, Object> map = new HashMap<>();
            map.put("type", 3);
            map.put("nick_name", member.getNickname());
            map.put("member_image", member.getFace());
            map.put("luck_prize", gotLuckPrize.getPrizeName());
            WechatSendMessage.WX_MESSAGE_EXECUTOR.execute(() -> {
                String requestPath = url + WEB_SOCKET;
                HttpUtils.httpPostByJson(requestPath, JSON.toJSON(map).toString(), JsonBean.class);
            });
        }

        return resultMap;
    }


    // 创建或修改抽奖活动
    @Override
    public void addOrUpdateLuck(LuckDO luckDO) {
        luckDO.setLuckStatus(LuckStatusEnums.NEW.getIndex());

        Seller seller = UserContext.getSeller();
        Clerk clerk = clerkManager.getClerkByMemberId(seller.getUid());

        // 根据前端是否传ID判断是更新还是新增
        Integer luckId = luckDO.getLuckId();
        if (luckId == null || luckId == 0) {
            luckDO.setSellerId(seller.getSellerId());
            luckDO.setSellerName(seller.getSellerName());
            luckDO.setCreateTime(DateUtil.getDateline());
            luckDO.setCreateName(clerk.getClerkName());
            this.daoSupport.insert(luckDO);
        } else {
            luckDO.setUpdateTime(DateUtil.getDateline());
            luckDO.setUpdateName(clerk.getClerkName());
            this.daoSupport.update(luckDO, luckId);
        }
    }

    // 删除抽奖活动
    @Override
    @Transactional(value = "systemTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public void deleteLuck(Integer luckId) {
        // 校验抽奖活动是否可以删除
        this.checkLuck(luckId);

        Seller seller = UserContext.getSeller();
        Clerk clerk = clerkManager.getClerkByMemberId(seller.getUid());

        // 删除抽奖活动
        String luckSql = "update es_luck set is_delete = 1 ,update_name = ?, update_time = ? where luck_id = ?";
        this.daoSupport.execute(luckSql, clerk.getClerkName(), DateUtil.getDateline(), luckId);

        // 删除该活动关联的抽奖商品
        String queryLuckPrizeSql = "select * from es_luck_prize where luck_id = ? and seller_id = ?";
        List<LuckPrizeDO> luckPrizeDOList = this.daoSupport.queryForList(queryLuckPrizeSql, luckId, seller.getSellerId());
        if (!CollectionUtils.isEmpty(luckPrizeDOList)) {
            String luckPrizeSql = "update es_luck_prize set is_delete = 1, update_time = ? where luck_prize_id in (?)";
            List<Integer> luckPrizeIdList = luckPrizeDOList.stream().map(luckPrizeDO -> luckPrizeDO.getLuckPrizeId()).collect(Collectors.toList());
            String luckPrizeIdString = StringUtil.listToString(luckPrizeIdList, ",");
            this.daoSupport.execute(luckPrizeSql, DateUtil.getDateline(), luckPrizeIdString);
        }
    }

    @Override
    public boolean checkButtonStatus(String orderSn) {
        if(StringUtil.isEmpty(orderSn)){
            return false;
        }

        // 获取最新上线的抽奖活动
        long time=DateUtil.getDateline();
        String sql="select * from es_luck where luck_status = ? and start_time <= ? and end_time >= ?";
        List<LuckDO> luckList = this.daoSupport.queryForList(sql,LuckDO.class,LuckStatusEnums.ONLINE.getIndex(),time,time);
        if (Collections.isEmpty(luckList)) {
            return false;
        }

        OrderDetailDTO orderDetailDTO=orderQueryManager.getModel(orderSn);
        if(ObjectUtils.isEmpty(orderDetailDTO)){
            return false;
        }

        Buyer buyer=UserContext.getBuyer();
        if(!buyer.getUid().equals(orderDetailDTO.getMemberId())){
            return false;
        }

        // 如果订单金额>=抽奖限制，则返回true，显示抽奖按钮
        if(luckList.get(0).getMinLimitPrice()<=orderDetailDTO.getOrderPrice()){
            return true;
        }

        return false;
    }

    // 校验抽奖活动是否可以删除
    private LuckDO checkLuck(Integer luckId) {
        if (luckId == null || luckId == 0) {
            throw new ServiceException("500", "参数异常");
        }

        LuckDO luckDO = this.daoSupport.queryForObject(LuckDO.class, luckId);

        if (luckDO == null) {
            throw new ServiceException("500", "抽奖活动不存在");
        }

        Seller seller = UserContext.getSeller();
        if (!luckDO.getSellerId().equals(seller.getSellerId())) {
            throw new ServiceException("500", "没有权限删除非本店铺的抽奖活动");
        }

        if (luckDO.getLuckStatus() == LuckStatusEnums.ONLINE.getIndex()) {
            throw new ServiceException("500", "不能删除已上线的抽奖活动");
        }

        return luckDO;
    }

    /**
     * 查询今日剩余抽奖次数
     */
    private int getTodayLeftLuckTimes(LuckDO luckDO, Integer memberId) {

        String sql="SELECT o.order_price,o.order_price-r.refund_price refundable_price,r.refund_status FROM es_order o \n" +
                "LEFT JOIN es_refund r ON o.sn=r.order_sn\n" +
                "where o.create_time >= ? and o.create_time <= ? and o.member_id=? " +
                "and o.pay_status=? and o.order_price >= ? and o.disabled=0 ";
        // 查询当天支付，且支付金额大于抽奖限制的订单
        List<LuckTotalTime> luckTotalTimeList=this.daoSupport.queryForList(sql, LuckTotalTime.class,DateUtil.startOfTodDay(),DateUtil.endOfTodDay(),memberId,
                PayStatusEnum.PAY_YES.name(),luckDO.getMinLimitPrice());
        if(!CollectionUtils.isEmpty(luckTotalTimeList)){
            // 过滤已经退款且退款之后金额大于抽奖限制的订单
            luckTotalTimeList=luckTotalTimeList.stream().filter(luckTotalTime -> check(luckTotalTime.getRefundStatus(),luckTotalTime.getRefundablePrice(),luckDO.getMinLimitPrice())).collect(Collectors.toList());
        }

        int todayTotalTimes = (luckTotalTimeList == null ? 0 : luckTotalTimeList.size());
        Integer dayMaxTimes = luckDO.getDayMaxTimes();
        todayTotalTimes = todayTotalTimes > dayMaxTimes ? dayMaxTimes : todayTotalTimes;

        QueryLuckRecordVO queryLuckRecordVO = new QueryLuckRecordVO();
        queryLuckRecordVO.setPageNo(1);
        queryLuckRecordVO.setPageSize(100);
        queryLuckRecordVO.setLuckId(luckDO.getLuckId());
        queryLuckRecordVO.setMemberId(memberId);
        queryLuckRecordVO.setStartCreateTime(DateUtil.startOfTodDay());
        queryLuckRecordVO.setEndCreateTime(DateUtil.endOfTodDay());
        Page<LuckRecordDO> luckRecordPage = luckRecordManager.queryLuckRecordList(queryLuckRecordVO);
        List<LuckRecordDO> luckRecordList = luckRecordPage.getData();
        int todayLuckedTimes = (luckRecordList == null ? 0 : luckRecordList.size());
        int todayLeftTimes = todayTotalTimes - todayLuckedTimes;
        return todayLeftTimes < 0 ? 0 : todayLeftTimes;
    }

    // 判断当前订单是否可以抽奖
    private boolean check(String refundStatus, Double refundablePrice, Double minLimitPrice) {
        if (!StringUtil.isEmpty(refundStatus)) {
            if (refundStatus.equals(RefundStatusEnum.COMPLETED.name()) || refundStatus.equals(RefundStatusEnum.PASS.name())
                    || refundStatus.equals(RefundStatusEnum.STOCK_IN.name()) || refundStatus.equals(RefundStatusEnum.REFUNDING.name())) {
                if (refundablePrice < minLimitPrice) {
                    return false;
                }
            }
        }
        return true;
    }


    /**
     * 概率重新分布
     */
    private void probabilityAutoSet(List<LuckPrizeDO> luckPrizeList) {
        BigDecimal reConfigProbability = BigDecimal.ZERO;

        LuckPrizeDO thanksLuckPrizeDO = null;
        for (LuckPrizeDO luckPrizeDO : luckPrizeList) {
            Integer prizeType = luckPrizeDO.getPrizeType();

            // 拿到谢谢参与奖品
            if (prizeType == PrizeTypeEnums.THANKS.getIndex()) {
                thanksLuckPrizeDO = luckPrizeDO;
                continue;
            }

            // 奖品数量小于0的概率需要重新分配
            Integer prizeNum = luckPrizeDO.getPrizeNum();
            if (prizeNum <= 0) {
                reConfigProbability = reConfigProbability.add(luckPrizeDO.getProbability());
            }
        }

        // 谢谢参与概率+待重新分配的概率
        if (thanksLuckPrizeDO != null && reConfigProbability.doubleValue() > 0) {
            BigDecimal thanksProbability = thanksLuckPrizeDO.getProbability();
            thanksLuckPrizeDO.setProbability(thanksProbability.add(reConfigProbability));
        }
    }

}
